Skip to main content
  1. Writing/

Peek Into Halo Infinite Playlist Weights - With The Halo Infinite API

·2266 words
This blog post is part of a series on exploring the Halo game API.

Introduction #

If you’ve played Halo Infinite for some time, you might’ve noticed that every game selection from the main menu is actually a playlist - a combination of different match modes that you can play with ever-so-slightly different configurations and maps.

My personal favorite playlist is Husky Raid - and honestly, why wouldn’t you jump into Husky Raid, the only game mode where you can easily hit an Overkill and Extermination combo with the Skewer?

So, each playlist can have different combinations of maps and game modes, collectively known as map-mode pairs. Given that there are different combinations within the same playlist, you might also have noticed that certain game modes and maps do occur a bit more frequently in a given playlist than others. That is controlled by weights - values that determine how likely a map-mode pair will be picked when you press Play.

But before we get into the weights, let’s talk a bit about the overall play structure. It can be defined as such, where the game can have one or more playlist, which in turn encompasses several map-mode pairs that represent a combination of game mode and a map:

graph LR; A[Game]-->B[Playlist] --> C[Map-Mode Pair A] & D[Map-Mode Pair B] & E[Map-Mode Pair C]; C --> F[Map] & G[Game Mode]; D --> F[Map] & I[Game Mode]; E --> H[Map] & K[Game Mode]; A[Game]-->L[Playlist] --> E & N[Map-Mode Pair E] & O[Map-Mode Pair F]; N --> I[Map] & K[Game Mode]; O --> T[Map] & K[Game Mode];

There are, of course, more than two playlists in the game, and then the chart would become even messier if you consider that every asset also has a version. The example above is purely illustrative and oversimplifies the breakdown.

Mole out of the ground, thinking.

Hey, in the example above Map-Mode Pair C belongs to both example playlists, and there is a mix of maps and modes crossing over many times. Is that accurate? How can that be true?

Indeed, that is correct. The same game mode and map combination can belong to more than one playlist. Every map can be used in many map-mode pairs, and every mode can, just like the maps, belong to more than one combo. To demonstrate this, let’s make our example less hypothetical and look at the current configuration, up-to-date as of build 6.10025.11703.0.

Where do playlists come from #

I’ll start off by mentioning that if you’ve used Fiddler to inspect the traffic from Halo Infinite, you likely noticed that the game does request data for each playlist when launched without the data already cached:

Halo Infinite playlist REST API requests as seen in Fiddler.
Halo Infinite playlist REST API requests as seen in Fiddler.

The problem is that there is seemingly not a pre-emptive call to get the list of playlists. So, where exactly does the game get the list of available playlists, especially since a few of them rotate weekly and we do not get a new build update weekly? The answer lies in a secondary protocol that is used by Halo Infinite that carries some of this information - Advanced Message Queuing Protocol (AMQP).

When the game starts, a connection is established to lobby-hi.svc.halowaypoint.com and some application/x-bond-compact-binary (i.e., Bond) data is being exchanged before the fireteam is established over a long-running WebSocket connection. I was actually thankful when I saw that it was WebSockets because WireShark was oddly silent when I tried to filter for AMQP traffic, but Fiddler was more than happy to help me intercept the binary material that was exchanged. After some trial and error with the help of my homebrew Bond Reader utility, trying every binary blob for Bond contents, I managed to extract some nuggets that seemed oddly familiar (the offset for this blob was a whopping 57 bytes that I needed to skip):

█░░░░░░░░░░░░░░░░░░░░░░░░░ STR ░░░░░░░░░░░░░░░░░░░░░░░░░█
Data type:       BT_UINT32  Field ID:               0
1200999239
Data type:       BT_UINT16  Field ID:               1
23346
Data type:       BT_UINT16  Field ID:               2
19591
Data type:       BT_UINT64  Field ID:               3
3587842982486911896
Data type:         BT_STOP  Field ID:               0
█░░░░░░░░░░░░░░░░░░░░░░░░░ EST ░░░░░░░░░░░░░░░░░░░░░░░░░█

Or, if you really want a quick-and-dirty check, see if you can pass these numbers through this C# snippet:

uint a = 1200999239;
ushort b = 23346;
ushort c = 19591;
byte[] d = BitConverter.GetBytes(3587842982486911896);

Console.WriteLine(new Guid(a, b, c, d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7]));

The result of that was 4795cb47-5b32-4c87-98ab-02f12e94ca31 - an asset GUID that identified one of the playlists that you saw in the request screenshot above. Bingo - we now know that the list of playlists comes through the AMQP pipe.

Mapping out existing playlists #

When the game boots for the first time, and if there are no cached playlists metadata entries already, it will request each playlist separately. In the latest build, there are 16 requests in the format:

https://discovery-infiniteugc.svc.halowaypoint.com
  /hi
  /playlists
  /{ASSET_ID}
  /versions
  /{VERSION_ID}
  ?clearanceId={FLIGHT_ID}

A request to this endpoint will yield a very long JSON response that we will dissect in a bit.

If you look at the earlier video, you will notice that there are quite a few options for a player to pick from when they want to play in Multiplayer mode. Mapping back to the 16 requests I mentioned earlier, there are currently 16 active playlists:

Playlist Name Asset ID Version ID
Infection 00cd3ab8-4b24-4181-8493-7aee34751f52 6ec76c9b-5134-4444-8e6a-b736413d0af3
Lone Wolves d22aa90d-3091-4214-a85e-c968037cef2f b5439adc-6100-44a3-bc37-012d618e8c9f
Squad Battle 52392a40-5a75-4205-abc6-b51cdc84918c dc1a0dde-24cf-4e26-a94f-0a5d76854959
Husky Raid da024c44-7c2a-49bb-a6ff-8d91ac179900 19bfb62e-76dd-46bf-ab6e-180e69b8873c
Team Slayer aa41f6a9-51be-4f25-a53f-48192ce14de7 a5e4c224-89cb-49f8-88e0-b2f04e74b59f
Super Fiesta 4829f027-a9af-4b2f-86dd-7b290d6bb0a4 9bd2d072-e579-4b54-b603-d4801111ce53
Ranked Slayer dcb2e24e-05fb-4390-8076-32a0cdb4326e d4165aad-2cc0-4130-a93a-4742f6606c0b
Quick Play bdceefb3-1c52-4848-a6b7-d49acd13109d 01949da1-ae38-460a-a27c-e95aac0db9b6
Big Team Battle dc4929de-216c-43bc-b207-1702253f4576 c487b0af-5b4a-4576-8ae7-34ff9773a20f
Tactical Slayer 70bb9184-e674-4307-8846-239ab4a30cb6 e2d74d94-9cec-4286-b4d8-ded9ccc8d858
Extraction 24/7 4795cb47-5b32-4c87-98ab-02f12e94ca31 58a0c3d9-d906-4c07-bc48-af757c0fe580
Team Doubles 73b48e1e-05c4-4004-927d-965549b28396 17b616fb-f128-46c9-b966-7850b38445f9
Ranked Arena edfef3ac-9cbe-4fa2-b949-8f29deafd483 5dfe7c2b-8d15-4049-933e-eb9c0fa113a6
Social Slayer f336c231-e55c-46c9-af11-d9acf1b3245d 0e498c69-56a9-43cd-b8af-a73d9f14d016
WORKSHOP BTB INFECTION 7071b932-18c1-4f9b-b80e-266aec1d6770 f5dd07b9-a22e-4ccc-a9a1-a489a0d1269f
Bot Bootcamp a446725e-b281-414c-a21e-31b8700e95a1 b108af37-38b3-45f4-af18-9e1f59f930b3

Now that we have actual data, let’s re-build the graphic that shows playlist association with map-mode pairs. To start, I am looking at the breakdown of game modes and the associated map-mode pairs, which is already producing a massive graph.

Associations between playlist and map-mode pairs #

There is a limited number of playlists, but all of them have quite a few map-mode pairs associated with them. In the current configuration, there is no map-mode pair overlap.

Because the graph is big, you can skip right to the next section.

Flow chart showing the connection between playlists and map-mode pairs in Halo Infinite.
Flow chart showing the connection between playlists and map-mode pairs in Halo Infinite.

Associations between map-mode pairs and maps #

What if we connected out map-mode pairs to maps now? Let’s try this with a slightly different pivot, because fitting all of the data in a single chart will require that chart to be the size of Times Square, and the arrows will blend in. There are less maps than map-mode pairs - one map can be included in many different game modes.

As an aside, looks like Launch Site has lost its initial appeal - you can only get it in Fiesta and Infection. And speaking of Launch Site, an eagle-eyed engineer spotted that Event:Infection on Launch Site is mapped to Bazaar. This is not a bug in the chart. The map-mode pair is actually called that despite the fact that MapLink points to Bazaar. This is very likely just a small issue with the configuration on the game content management system (CMS) side.

To learn more about how game modes snap to individual map-mode pairs, you can skip to the next section.

Flow chart showing the connection between map-mode pairs and maps in Halo Infinite.
Flow chart showing the connection between map-mode pairs and maps in Halo Infinite.

Associations between map-mode pairs and game variants #

Lastly, let’s take a look at how individual game modes are associated with map-mode pairs. I don’t think the breakdown will come as a surprise to anyone who’s been playing Halo Infinite in more than one game mode.

Skip to the next section to learn about exact map-mode pair weights.

Flow chart showing the connection between map-mode pairs and game modes in Halo Infinite.
Flow chart showing the connection between map-mode pairs and game modes in Halo Infinite.

The weights #

Above, we looked at how playlists are associated with map-mode pairs, which in turn represent a combination of a map and game mode. But not every combination is made equal within a playlist, and the first clue to that is given to us if we look inside the JSON response when obtaining playlist metadata (all within CustomData\PlaylistEntries). Let’s use Infection as an example:

"PlaylistEntries": [
    {
        "MapModePairAssetId": "b347c8c1-be08-4fc8-8d76-61cccde62be3",
        "Metadata": {
            "Weight": 60.0
        }
    },
    {
        "MapModePairAssetId": "e3777308-c0cc-4e4d-bbcd-dfe7d026c7e1",
        "Metadata": {
            "Weight": 60.0
        }
    },
    {
        "MapModePairAssetId": "38c98e94-2e3e-4f5b-8801-770b6935f849",
        "Metadata": {
            "Weight": 60.0
        }
    },
    {
        "MapModePairAssetId": "4cafa572-74cd-4333-9629-b9c1915bccfd",
        "Metadata": {
            "Weight": 60.0
        }
    },
    {
        "MapModePairAssetId": "8b91c712-989a-4e1e-9609-7afbb11f0dbd",
        "Metadata": {
            "Weight": 60.0
        }
    },
    {
        "MapModePairAssetId": "482f7c56-0d18-4b72-b736-5521bc3ba191",
        "Metadata": {
            "Weight": 60.0
        }
    },
    {
        "MapModePairAssetId": "17a3fe3d-3ff0-4238-bf3c-05012e421ae7",
        "Metadata": {
            "Weight": 60.0
        }
    },
    {
        "MapModePairAssetId": "26b0efcd-c113-411b-ab1b-b1fd8bf91299",
        "Metadata": {
            "Weight": 60.0
        }
    },
    {
        "MapModePairAssetId": "10eb5282-c451-48dc-be8e-6e96393f5788",
        "Metadata": {
            "Weight": 145.0
        }
    },
    {
        "MapModePairAssetId": "ca1b98fa-2dd1-4e8a-a8a6-fea4d14455a6",
        "Metadata": {
            "Weight": 145.0
        }
    },
    {
        "MapModePairAssetId": "e2ec56af-44b0-4e31-a6c9-090762dfcfb3",
        "Metadata": {
            "Weight": 260.0
        }
    },
    {
        "MapModePairAssetId": "4729a201-9942-4835-bb92-59bfce8a5a58",
        "Metadata": {
            "Weight": 260.0
        }
    },
    {
        "MapModePairAssetId": "4c93ef81-342e-4071-8744-044311721df9",
        "Metadata": {
            "Weight": 145.0
        }
    }
]

Or, if we look at that in a slightly more readable format:

Name Asset ID Asset Version Weight
Event:Infection on Launch Site e3777308-c0cc-4e4d-bbcd-dfe7d026c7e1 37db76e4-eb41-4722-9f17-f965de8903f2 60
Event:Infection on Aquarius b347c8c1-be08-4fc8-8d76-61cccde62be3 c95cc9d5-b120-4f41-b8a5-cbfc8d4fb012 60
Event:Infection on Cliffhanger 8b91c712-989a-4e1e-9609-7afbb11f0dbd 184f9001-6bb2-4900-a177-cf0a033a7c23 60
Event:Infection on Forest 482f7c56-0d18-4b72-b736-5521bc3ba191 9e6cba33-b7b3-447d-9123-ed3578e1c4d2 60
Arena:Infection on Launch Site 10eb5282-c451-48dc-be8e-6e96393f5788 90f0f63f-e758-4a44-8327-60b8a1f2b435 145
Arena:Infection on Prism 4729a201-9942-4835-bb92-59bfce8a5a58 4336e873-abbf-4df9-bdd0-e36df8e6a9ec 260
Event:Infection on Recharge 17a3fe3d-3ff0-4238-bf3c-05012e421ae7 35e58535-22a2-48c1-9201-a84f1edb11d3 60
Event:Infection on Streets 26b0efcd-c113-411b-ab1b-b1fd8bf91299 2877f4ad-487c-4cc4-8036-85ecc1aa9ab2 60
Arena:Infection on Live Fire ca1b98fa-2dd1-4e8a-a8a6-fea4d14455a6 30dcf5d7-a9fc-4f32-b49a-ecd5339a6604 145
Event:Infection on Chasm 4cafa572-74cd-4333-9629-b9c1915bccfd 4533f06c-6071-439c-857d-db7ea890602a 60
Arena:Infection on Catalyst 4c93ef81-342e-4071-8744-044311721df9 bd7c6e7a-93dd-418c-9708-0bcf7a0b7582 145
Arena:Infection on Forbidden e2ec56af-44b0-4e31-a6c9-090762dfcfb3 86eec4a9-34b4-482d-b663-7bd930cb8464 260
Event:Infection on Behemoth 38c98e94-2e3e-4f5b-8801-770b6935f849 fd4969e2-ce21-4b1f-a582-dcce4aead780 60

If you are playing Infection, you are much more likely to play on Forbidden and Prism (the new maps introduced in Season 5), followed by Catalyst, Live Fire, and Launch Site. Then there’s all the other maps.

Mole looking through a magnifying glass.

So, looking at the weights, it doesn't seem like they are at all representative of percentages, are they? These are just arbitrary values within the playlist to represent the relative weight?

Indeed, it seems to be that way! If we look at other playlists, such as Lone Wolves, we see values in the range from 30 to 275, so even higher than what we see in Infection. This tells me that the weights are likely (keep in mind - I don’t work at 343 Industries and all knowledge is extrapolation from public API data) relative within the playlist.

To visualize this better, let’s take a look at how all of the playlists stack-up weight-wise today. My running assumption is that if the weight is relative, then we can also compute the percentage likelihood of a given combination to land in a given game.

Hover over the slices in the doughnut to see the values.

Infection #

Forbidden and Prism are the stars of the show, by a pretty large margin.

Lone Wolves #

The playlist is mostly balanced. There is a slight increase in weights for map-mode pairs on Forbidden and Prism, but not much compared to the wide array of others.

Squad Battle #

Fun fact - this playlist is perfectly balanced, because every map-mode pair has equal weight (100).

Husky Raid #

What do you know - another perfectly balanced playlist.

Team Slayer #

Modes that use newer maps (Forbidden and Prism) have ever so slightly higher likelihood of popping up, but that can be attributed to the desire to likely give players more time to get accustomed to the environment (as well as test the maps for playability).

Super Fiesta #

Lots of maps to choose from, with a bit of bias towards Super Fiesta (i.e., Fiesta with campaign weapons).

Ranked Slayer #

Slayer on Forbidden is the only map-mode pair that is slightly heavier weighted than others. Otherwise, it’s equally balanced.

Quick Play #

The developers really want you to play Strongholds on Streets and King Of The Hill on Argyle. I don’t disagree with that decision.

Big Team Battle #

Big Team Battle (BTB) is not my cup of tea, but if it’s yours - you’re more likely to play BTB Slayer on Scarr, Oasis, Fragmentation, Highpower, Deadlock, and Breaker.

Tactical Slayer #

I like Tactical Slayer (i.e., SWAT) because it really forces me to improve my reaction times. I also got used to playing with the Battle Rifle (BR) instead of the Bandit. Lucky for me, the skew (at least for now) is still in favor of BR-based SWAT.

Extraction 24/7 #

No surprises here - new mode, with new maps (Forbidden and Prism) being more heavily weighted.

Team Doubles #

You’re significantly more likely to play any mode that is not Slayer in this playlist.

Ranked Arena #

It took me some time to get used to non-Slayer game modes. That said, Slayer on Aquarius is still the most heavily-weighted map-mode pair in this playlist. Color me surprised.

Social Slayer #

Prepare to play some Mini Slayer on both small and large maps.

WORKSHOP BTB INFECTION #

No comment - it’s basically three maps for large-scale Infection.

Bot Bootcamp #

You’ll be playing bots on Forbidden and Prism.

Conclusion #

The more I started digging through this data, the more I realized that I want to be able to track this over time, similar to how I track my own match performance. That could be a project for OpenSpartan that I will (somehow) will need to find time for. Not only that, but looks like I need to be learning more about AMQP if I want to have an up-to-date view of currently active playlist.